﻿#region License

// This file is part of Grawlix.
// 
// Grawlix is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
// Grawlix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with Grawlix.  If not, see
// <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Grawlix.Services;

namespace Grawlix.Application
{
	/// <summary>
	/// Convenience base class for Governor objects.
	/// </summary>
	[Serializable]
	public abstract class GovernorBase :
		PermanentMarshalByRefObject, IGovernor
	{
		/// <summary>
		/// The kernel used to reference services.
		/// </summary>
		private readonly IGameKernel _gameKernel;

		/// <summary>
		/// Initializes a new instance of the 
		/// <see cref="GovernorBase"/> class.
		/// </summary>
		/// <param name="gameKernel">The game kernel to acquire
		/// services from.</param>
		/// <exception cref="ArgumentNullException"><c>gameKernel</c>
		/// is null.</exception>
		protected GovernorBase(IGameKernel gameKernel)
		{
			if (gameKernel == null)
				throw new ArgumentNullException("gameKernel");
			_gameKernel = gameKernel;
		}

		/// <summary>
		/// Gets the game kernel.
		/// </summary>
		/// <value>The game kernel to acquire services from.</value>
		public IGameKernel GameKernel
		{
			get { return _gameKernel; }
		}

		/// <summary>
		/// Responds to a message of a specified type.
		/// </summary>
		/// <param name="message">The message to respond to.</param>
		/// <remarks>Called as a multimethod.</remarks>
		[SuppressMessage("Microsoft.Performance", "CA1822",
			Justification = "Called as a multimethod.")]
		[SuppressMessage("Microsoft.Performance", "CA1801",
			Justification = "Called as a multimethod.")]
		public void HandleMessage(Message message)
		{
		}

		/// <summary>
		/// Responds to the specified message.
		/// </summary>
		/// <param name="message">The message to respond to.</param>
		public void SendMessage(Message message)
		{
			// TODO: Use compile-time weaving instead of reflection.
			const BindingFlags flags =
				BindingFlags.Instance | BindingFlags.Public;
			var types = new[] {message.GetType()};
			var modifiers = new ParameterModifier[] {};
			MethodInfo method = GetType().GetMethod("HandleMessage",
			                                        flags, null,
			                                        types, modifiers);
			Debug.Assert(method != null);
			method.Invoke(this, new object[] {message});
		}

		/// <summary>
		/// Updates the governor.
		/// </summary>
		/// <param name="elapsed">The elapsed time since last update.
		/// </param>
		public virtual void Update(TimeSpan elapsed)
		{
		}
	}
}